
package xj.graph2d.app.diagram;

import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.Map;

import xj.graph2d.Document;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.GraphEdgeBase;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.ModelListener;
import xj.graph2d.app.DrawApp;
import xj.graph2d.layout.DiagramLayout;
import xj.graph2d.layout.StateDiagramLayout;
import xj.graph2d.shapes.uml.CompoundStateShape;
import xj.graph2d.shapes.uml.OrthogonalStateShape;
import xj.graph2d.shapes.uml.SimpleStateShape;
import xj.graph2d.shapes.uml.StateShape;
import xj.graph2d.template.UMLTemplate;
import xj.meta.ModelElement;
import xj.model.uml.State;
import xj.model.uml.Transition;

public class StateDiagramBuilder extends DiagramBuilder { 

  public StateDiagramBuilder(DrawApp main, String name) { 
    this(main, name, false);
  }

  public StateDiagramBuilder(DrawApp main, String name, String topMachineName) { 
    super(main, name, Document.DocumentType.UML_State_Diagram);

    DiagramLayout layoutManager = new StateDiagramLayout(diagram); 
    diagram.setGraphLayoutManager(layoutManager);

    if (topMachineName != null) { 
      addStateMachine(topMachineName);
      this.topMachine = true;
      this.topMachineName = topMachineName;   
    } else { 
      this.topMachine = false;
      this.topMachineName = null;                 
    }
  }

  public StateDiagramBuilder(DrawApp main, String name, boolean topMachine) { 
    super(main, name, Document.DocumentType.UML_State_Diagram);

    DiagramLayout layoutManager = new StateDiagramLayout(diagram); 
    diagram.setGraphLayoutManager(layoutManager);

    if (topMachine) { 
      addStateMachine(name);
      this.topMachine = true;
      this.topMachineName = name;       
    } else { 
      this.topMachine = false;
      this.topMachineName = null;             
    }
  }

  public static GraphNodeBase makeStateShape(String name, 
					     State.StateType type, 
					     int nRegion) { 
    String tempname = "UMLStateDiagram.State1"; 
    switch (type) {
    case Initial_State:
      tempname = "UMLStateDiagram.InitialState"; 
      break;
    case Final_State:
      tempname = "UMLStateDiagram.FinalState"; 
      break;
    case Hyper_State:
      tempname = "UMLStateDiagram.CompoundState1"; 
      break;
    case Orthogonal_State:
      tempname = "UMLStateDiagram.OrthogonalState1"; 
      break;
    case Submachine_State:
      tempname = "UMLStateDiagram.StateMachine"; 
      break;
    default: 
      tempname = "UMLStateDiagram.State1"; 
    } 
    DrawShape newShape = makeShape(tempname);
    GraphNodeBase state = null;
    if (newShape instanceof SimpleStateShape) { 
      SimpleStateShape s = (SimpleStateShape) newShape;
      s.setStateName(name);
      s.fitCompartments();
      s.doLayout();
      state = s; 
    } else if (newShape instanceof OrthogonalStateShape) { 
      OrthogonalStateShape s = (OrthogonalStateShape) newShape;
      if (nRegion < 1) {
	nRegion = 1;
      }
      if (nRegion != 2) { 	  
	s.setNumberOfRegions(nRegion);
      }
      s.setStateName(name);
      state = s; 
      
    } else if (newShape instanceof CompoundStateShape) { 
      CompoundStateShape s = (CompoundStateShape) newShape;
      s.setStateName(name);
      s.layoutComponents();
      state = s; 
    } else if (newShape instanceof GraphNodeBase) { 
      state = (GraphNodeBase) newShape;
    }
    return state;
  }

  public static GraphEdgeBase makeTransitionShape() { 
    String tempname = "UMLStateDiagram.Transition1"; 
    GraphEdgeBase edge = (GraphEdgeBase) makeShape(tempname); 
    edge.setEdgeType(UMLTemplate.TRANSITION_EDGE);
    return edge;
  }

  public SimpleStateShape addSimpleState(String name) { 
    return (SimpleStateShape) addState(name, State.StateType.Simple_State, 0, null, 0);
  }

  public SimpleStateShape addSimpleState(String name, String parent) { 
    return (SimpleStateShape) addState(name, State.StateType.Simple_State, 0, parent, 0);
  }

  /*
   *  If the parent is an orthogonal state, r is the region 
   *  to which the new state will be added.  
   */
  public SimpleStateShape addSimpleState(String name, String parent, int r) { 
    return (SimpleStateShape) addState(name, State.StateType.Simple_State, 0, parent, r);
  }

  public CompoundStateShape addStateMachine(String name) { 
    return (CompoundStateShape) addState(name, State.StateType.Submachine_State, 0, null, 0);
  }

  public CompoundStateShape addStateMachine(String name, String parent) { 
    return (CompoundStateShape) addState(name, State.StateType.Submachine_State, 0, parent, 0);
  }

  public CompoundStateShape addCompoundState(String name) { 
    return (CompoundStateShape) addState(name, State.StateType.Hyper_State, 0, null, 0);
  }

  public CompoundStateShape addCompoundState(String name, String parent) { 
    return (CompoundStateShape) addState(name, State.StateType.Hyper_State, 0, parent, 0);
  }

  /*
   *  If the parent is an orthogonal state, r is the region 
   *  to which the new state will be added.  
   */
  public CompoundStateShape addCompoundState(String name, String parent, int r) { 
    return (CompoundStateShape) addState(name, State.StateType.Hyper_State, 0, parent, r);
  }

  /*
   *  nRegion is the number of regions to be created for the new state
   */
  public OrthogonalStateShape addOrthogonalState(String name, int nRegion) { 
    return (OrthogonalStateShape) addState(name, State.StateType.Orthogonal_State, nRegion, null, 0);
  }

  /*
   *  nRegion is the number of regions to be created for the new state
   */
  public OrthogonalStateShape addOrthogonalState(String name, int nRegion, String parent) { 
    return (OrthogonalStateShape) addState(name, State.StateType.Orthogonal_State, nRegion, parent, 0);
  }

  /*
   *  nRegion is the number of regions to be created for the new state
   *
   *  If the parent is an orthogonal state, r is the region 
   *  to which the new state will be added.  
   */
  public OrthogonalStateShape addOrthogonalState(String name, int nRegion, String parent, int r) { 
    return (OrthogonalStateShape) addState(name, State.StateType.Orthogonal_State, nRegion, parent, r);
  }

  public GraphNodeBase addInitialState(String name) { 
    return addState(name, State.StateType.Initial_State, 0, null, 0);
  }

  public GraphNodeBase addInitialState(String name, String parent) { 
    return addState(name, State.StateType.Initial_State, 0, parent, 0);
  }

  /*
   *  If the parent is an orthogonal state, r is the region 
   *  to which the new state will be added.  
   */
  public GraphNodeBase addInitialState(String name, String parent, int r) { 
    return addState(name, State.StateType.Initial_State, 0, parent, r);
  }

  public GraphNodeBase addFinalState(String name) { 
    return addState(name, State.StateType.Final_State, 0, null, 0);
  }

  public GraphNodeBase addFinalState(String name, String parent) { 
    return addState(name, State.StateType.Final_State, 0, parent, 0);
  }

  /*
   *  If the parent is an orthogonal state, r is the region 
   *  to which the new state will be added.  
   */
  public GraphNodeBase addFinalState(String name, String parent, int r) { 
    return addState(name, State.StateType.Final_State, 0, parent, r);
  }

  /*
   *  If the new state is an orthogonal state, nRegion is the number of 
   *  regions to be created for the new state
   *
   *  If the parent state is an orthogonal state, r is the region 
   *  to which the new state will be added.  
   */
  public GraphNodeBase addState(String name, State.StateType type, int nRegion, 
				String parent, int r) { 
    if (diagram != null) { 
      GraphNodeBase state = makeStateShape(name, type, nRegion);

      if (topMachine && 
	  topMachineName != null) { 
	String prefix = topMachineName + ".";
	if (parent == null) { 
	  parent = topMachineName; 
	} else if (!parent.startsWith(prefix)) {  
	  parent = prefix + parent;
	}
      }

      if (parent != null) {
	GraphNodeBase pnode = stateMap.get(parent);
	if (pnode instanceof OrthogonalStateShape) { 
	  OrthogonalStateShape pstate = (OrthogonalStateShape) pnode;
	  pstate.addShape(state);
	  //float y = pstate.getRegionTop(r);
	  //state.move(0, y);
	  Point2D cr = pstate.getRegionCenter(r);
	  Point2D cs = state.getCenterPosition(); 
	  float dx = (float) (cr.getX() - cs.getX());
	  float dy = (float) (cr.getY() - cs.getY());
	  state.move(dx, dy);

	  System.out.println("StateDiagramBuilder.addState() OrthogonalState name=" + name + 
			     "  dy=" + dy + "  dx=" + dx + 
			     "  p1=" + state.getEnd1());

	  diagram.shapeAdded(state);
	  stateMap.put(parent + "[" + r + "]." + name, state);
	} else if (pnode instanceof CompoundStateShape) { 
	  CompoundStateShape pstate = (CompoundStateShape) pnode;
	  pstate.addShape(state);
	  diagram.shapeAdded(state);
	  stateMap.put(parent + "." + name, state);
	} else { 
	  
	}
      } else { 
	diagram.addNode(state);
	stateMap.put(name, state);
      }
      return state; 
    }
    return null;
  }

  public void addTransition(String from, String to) { 
    addTransition(from, to, null, null, null);  
  }

  public void addTransition(String from, String to,
			    String event, String guard, String action) { 
    if (from != null && to != null) { 
      if (topMachine && 
	  topMachineName != null) { 
	String prefix = topMachineName + ".";
	if (!from.startsWith(prefix)) {  
	  from = prefix + from; 
	} 
	if (!to.startsWith(prefix)) {  
	  to = prefix + to; 
	} 	
      }

      GraphNodeBase fromState = stateMap.get(from);
      GraphNodeBase toState = stateMap.get(to);
      if (fromState != null && 
	  toState != null) { 
	GraphEdgeBase edge = makeTransitionShape();

	if (true) { 
	  String fromName = "-", toName = "-";
	  if (fromState instanceof StateShape) { 
	    StateShape s1 = (StateShape) fromState;
	    fromName = s1.getStateName();
	  }
	  if (toState instanceof StateShape) { 
	    StateShape s1 = (StateShape) toState;
	    toName = s1.getStateName();
	  }
	  System.out.println("StateDiagramBuilder.addTransition() " + fromName + " " + toName);
	}

	if (event != null || 
	    guard != null ||
	    action != null) { 
	  ModelElement model = edge.getModel();
	  if (model instanceof Transition) { 
	    Transition tmodel = (Transition) model; 
	    if (event != null) { 
	      tmodel.setEventName(event);
	    }
	    if (guard != null) { 
	      tmodel.setGuardText(guard);
	    }
	    if (action != null) { 
	      tmodel.setActionBody(action);
	    }
	    edge.updateShape();
	  }
	}

	addEdge(edge, fromState, toState);
      }
    }
  }

  public GraphNodeBase findState(String name) {
    if (name != null) { 
      if (topMachine && 
	  topMachineName != null) { 
	String prefix = topMachineName + ".";
	if (!name.startsWith(prefix)) {  
	  name = prefix + name; 
	} 	
      }
      return stateMap.get(name);
    }
    return null; 
  }

  public void clearDiagram(){
    stateMap.clear();
    diagram.removeAllNodes();
    diagram.removeAll();
  }

  protected Map<String, GraphNodeBase> stateMap = new HashMap<String, GraphNodeBase>();
  protected boolean topMachine = false;
  protected String topMachineName = null;

}

